winsafe\kernel\utilities/
w_string.rs1use std::cmp::Ordering;
2
3use crate::co;
4use crate::decl::*;
5use crate::guard::*;
6use crate::kernel::ffi;
7use crate::prelude::*;
8
9#[derive(Default, Clone)]
20pub struct WString {
21 buf: Buffer,
22}
23
24impl std::fmt::Display for WString {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 let txt = match self.buf.to_string_checked() {
27 Ok(t) => t,
28 Err(e) => format!("PARSING ERROR: {}", e.to_string()),
29 };
30 std::fmt::Display::fmt(&txt, f)
31 }
32}
33impl std::fmt::Debug for WString {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 std::fmt::Debug::fmt(&self.buf, f)
36 }
37}
38
39impl std::cmp::PartialEq for WString {
40 fn eq(&self, other: &Self) -> bool {
41 self.cmp(other) == Ordering::Equal
42 }
43}
44impl std::cmp::Eq for WString {}
45
46impl std::cmp::PartialOrd for WString {
47 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
48 let ord = unsafe { ffi::lstrcmpW(self.as_ptr(), other.as_ptr()) };
49 Some(if ord < 0 {
50 Ordering::Less
51 } else if ord > 0 {
52 Ordering::Greater
53 } else {
54 Ordering::Equal
55 })
56 }
57}
58impl std::cmp::Ord for WString {
59 fn cmp(&self, other: &Self) -> Ordering {
60 self.partial_cmp(other).unwrap()
61 }
62}
63
64impl WString {
65 pub const SSO_LEN: usize = Buffer::SSO_LEN;
68
69 #[must_use]
73 pub fn from_opt_str(s: Option<impl AsRef<str>>) -> Self {
74 Self { buf: Buffer::from_opt_str(s) }
75 }
76
77 #[must_use]
81 pub fn from_str(s: impl AsRef<str>) -> Self {
82 Self { buf: Buffer::from_str(s, ForceHeap::No) }
83 }
84
85 #[must_use]
92 pub fn from_str_force_heap(s: impl AsRef<str>) -> Self {
93 Self { buf: Buffer::from_str(s, ForceHeap::Yes) }
94 }
95
96 #[must_use]
103 pub fn from_str_vec(v: &[impl AsRef<str>]) -> Self {
104 Self { buf: Buffer::from_str_vec(v) }
105 }
106
107 #[must_use]
112 pub fn from_wchars_count(src: *const u16, num_chars: usize) -> Self {
113 Self {
114 buf: Buffer::from_wchars_count(src, num_chars),
115 }
116 }
117
118 #[must_use]
127 pub unsafe fn from_wchars_nullt(src: *const u16) -> Self {
128 Self { buf: Buffer::from_wchars_nullt(src) }
129 }
130
131 #[must_use]
135 pub fn from_wchars_slice(src: &[u16]) -> Self {
136 Self { buf: Buffer::from_wchars_slice(src) }
137 }
138
139 #[must_use]
141 pub const fn new() -> Self {
142 Self { buf: Buffer::new() }
143 }
144
145 #[must_use]
148 pub fn new_alloc_buf(sz: usize) -> Self {
149 Self {
150 buf: Buffer::new_alloc_buf(sz, ForceHeap::No),
151 }
152 }
153
154 #[must_use]
167 pub unsafe fn as_mut_ptr(&mut self) -> *mut u16 {
168 self.buf.as_mut_ptr()
169 }
170
171 #[must_use]
173 pub fn as_mut_slice(&mut self) -> &mut [u16] {
174 self.buf.as_mut_slice()
175 }
176
177 #[must_use]
184 pub fn as_ptr(&self) -> *const u16 {
185 self.buf.as_ptr()
186 }
187
188 #[must_use]
190 pub fn as_slice(&self) -> &[u16] {
191 self.buf.as_slice()
192 }
193
194 #[must_use]
199 pub const fn buf_len(&self) -> usize {
200 self.buf.buf_len()
201 }
202
203 pub fn copy_to_slice(&self, dest: &mut [u16]) {
210 if !dest.is_empty() {
211 let usable_len = dest.len() - 1; self.as_slice()
213 .iter()
214 .zip(dest[..usable_len].iter_mut())
215 .for_each(|(src, dest)| *dest = *src);
216 dest[usable_len..]
217 .iter_mut()
218 .for_each(|dest| *dest = 0x0000); }
220 }
221
222 pub fn fill_with_zero(&mut self) {
224 self.as_mut_slice().iter_mut().for_each(|ch| *ch = 0x0000);
225 }
226
227 #[must_use]
229 pub const fn is_allocated(&self) -> bool {
230 self.buf.is_allocated()
231 }
232
233 #[must_use]
241 pub fn to_string_checked(&self) -> Result<String, std::string::FromUtf16Error> {
242 self.buf.to_string_checked()
243 }
244
245 #[must_use]
251 pub fn str_len(&self) -> usize {
252 unsafe { ffi::lstrlenW(self.buf.as_ptr()) as _ }
253 }
254
255 pub fn make_lowercase(&mut self) {
258 unsafe {
259 ffi::CharLowerW(self.as_mut_ptr());
260 }
261 }
262
263 pub fn make_uppercase(&mut self) {
266 unsafe {
267 ffi::CharUpperW(self.as_mut_ptr());
268 }
269 }
270
271 #[must_use]
297 pub fn parse(data: &[u8]) -> SysResult<Self> {
298 let mut data = data;
299 if data.is_empty() {
300 return Ok(Self::new()); }
302
303 let (encoding, sz_bom) = Encoding::guess(data);
304 data = &data[sz_bom..]; Ok(Self::from_wchars_slice(&match encoding {
307 Encoding::Ansi => Self::parse_ansi(data),
308 Encoding::Win1252 => MultiByteToWideChar(co::CP::WINDOWS_1252, co::MBC::NoValue, data)?,
309 Encoding::Utf8 => MultiByteToWideChar(co::CP::UTF8, co::MBC::NoValue, data)?,
310 Encoding::Utf16be => Self::parse_utf16(data, true),
311 Encoding::Utf16le => Self::parse_utf16(data, false),
312 Encoding::Utf32be
313 | Encoding::Utf32le
314 | Encoding::Scsu
315 | Encoding::Bocu1
316 | Encoding::Unknown => panic!("Encoding {} not implemented.", encoding),
317 }))
318 }
319
320 fn parse_ansi(data: &[u8]) -> Vec<u16> {
321 data.iter()
322 .take_while(|ch| **ch != 0x0000) .map(|ch| *ch as u16) .collect()
325 }
326
327 fn parse_utf16(data: &[u8], is_big_endian: bool) -> Vec<u16> {
328 let data = if data.len() % 2 == 1 {
329 &data[..data.len() - 1] } else {
331 data
332 };
333
334 data.chunks(2)
335 .take_while(|ch2| **ch2 != [0x00, 0x00]) .map(|ch2| {
337 if is_big_endian {
338 u16::from_be_bytes(ch2.try_into().unwrap())
339 } else {
340 u16::from_le_bytes(ch2.try_into().unwrap())
341 }
342 })
343 .collect()
344 }
345}
346
347#[derive(PartialEq, Eq)]
348enum ForceHeap {
349 Yes,
350 No,
351}
352
353enum Buffer {
354 Stack([u16; Self::SSO_LEN]),
355 Heap(usize, GlobalFreeGuard), Unallocated,
357}
358
359impl Default for Buffer {
360 fn default() -> Self {
361 Self::Unallocated
362 }
363}
364
365impl Clone for Buffer {
366 fn clone(&self) -> Self {
367 match self {
368 Self::Unallocated => Self::Unallocated,
369 _ => {
370 let mut new_self = Self::new_alloc_buf(self.buf_len(), ForceHeap::No);
371 self.as_slice()
372 .iter()
373 .zip(new_self.as_mut_slice())
374 .for_each(|(src, dest)| *dest = *src);
375 new_self
376 },
377 }
378 }
379}
380
381impl std::fmt::Debug for Buffer {
382 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
383 let txt = match self.to_string_checked() {
384 Ok(t) => t,
385 Err(e) => format!("PARSING ERROR: {}", e.to_string()),
386 };
387 write!(
388 f,
389 "{}",
390 match self {
391 Self::Stack(_) => format!("STACK({}) \"{}\"", self.buf_len(), txt),
392 Self::Heap(_, _) => format!("HEAP({}) \"{}\"", self.buf_len(), txt),
393 Self::Unallocated => "UNALLOCATED \"\"".to_owned(),
394 }
395 )
396 }
397}
398
399impl Buffer {
400 pub const SSO_LEN: usize = 20;
401
402 #[must_use]
403 fn from_opt_str(s: Option<impl AsRef<str>>) -> Self {
404 match s {
405 Some(s) => Self::from_str(s, ForceHeap::No),
406 None => Self::Unallocated,
407 }
408 }
409
410 #[must_use]
411 fn from_str(s: impl AsRef<str>, force_heap: ForceHeap) -> Self {
412 let s_len = s.as_ref().encode_utf16().count();
413 if s_len == 0 {
414 Self::Unallocated
415 } else {
416 let num_chars = s_len + 1; let mut new_self = Self::new_alloc_buf(num_chars, force_heap);
418 s.as_ref()
419 .encode_utf16()
420 .zip(new_self.as_mut_slice())
421 .for_each(|(src, dest)| *dest = src);
422 new_self
423 }
424 }
425
426 #[must_use]
427 fn from_str_vec(v: &[impl AsRef<str>]) -> Self {
428 let tot_chars = v.iter() .fold(0, |tot, s| tot + s.as_ref().chars().count() + 1) + 1; let mut new_self = Self::new_alloc_buf(tot_chars, ForceHeap::No);
432 v.iter()
433 .map(|s| {
434 s.as_ref().encode_utf16().chain(std::iter::once(0x0000)) })
436 .flatten()
437 .zip(new_self.as_mut_slice())
438 .for_each(|(src, dest)| *dest = src);
439 new_self
440 }
441
442 #[must_use]
443 fn from_wchars_count(src: *const u16, num_chars: usize) -> Self {
444 if src.is_null() || num_chars == 0 {
445 Self::Unallocated
446 } else {
447 Self::from_wchars_slice(unsafe { std::slice::from_raw_parts(src, num_chars) })
448 }
449 }
450
451 #[must_use]
452 unsafe fn from_wchars_nullt(src: *const u16) -> Self {
453 Self::from_wchars_count(src, unsafe { ffi::lstrlenW(src) as _ })
454 }
455
456 #[must_use]
457 fn from_wchars_slice(src: &[u16]) -> Self {
458 if src.is_empty() {
459 Self::Unallocated
460 } else {
461 let num_chars = src
462 .iter()
463 .take_while(|ch| **ch != 0x0000) .count() + 1; let mut new_self = Self::new_alloc_buf(num_chars, ForceHeap::No);
466 src.iter()
467 .take_while(|ch| **ch != 0x0000) .zip(new_self.as_mut_slice())
469 .for_each(|(src, dest)| *dest = *src);
470 new_self
471 }
472 }
473
474 #[must_use]
475 const fn new() -> Self {
476 Self::Unallocated
477 }
478
479 #[must_use]
480 fn new_alloc_buf(num_chars: usize, force_heap: ForceHeap) -> Self {
481 if num_chars == 0 {
482 Self::Unallocated
483 } else if force_heap == ForceHeap::Yes || num_chars > Self::SSO_LEN {
484 Self::Heap(
485 num_chars * std::mem::size_of::<u16>(),
486 HGLOBAL::GlobalAlloc(
487 co::GMEM::FIXED | co::GMEM::ZEROINIT,
488 num_chars * std::mem::size_of::<u16>(),
489 )
490 .unwrap(), )
492 } else {
493 Self::Stack([0x0000; Self::SSO_LEN])
494 }
495 }
496
497 #[must_use]
498 unsafe fn as_mut_ptr(&mut self) -> *mut u16 {
499 match self {
500 Self::Stack(arr) => arr.as_mut_ptr(),
501 Self::Heap(_, ptr) => ptr.ptr() as _,
502 Self::Unallocated => panic!("Trying to use an unallocated WString buffer."),
503 }
504 }
505
506 #[must_use]
507 fn as_mut_slice(&mut self) -> &mut [u16] {
508 match self {
509 Self::Stack(arr) => arr,
510 Self::Heap(_, ptr) => unsafe {
511 std::slice::from_raw_parts_mut(ptr.ptr() as _, self.buf_len())
512 },
513 Self::Unallocated => &mut [],
514 }
515 }
516
517 #[must_use]
518 fn as_ptr(&self) -> *const u16 {
519 match self {
520 Self::Stack(arr) => arr.as_ptr(),
521 Self::Heap(_, ptr) => ptr.ptr() as _,
522 Self::Unallocated => std::ptr::null(),
523 }
524 }
525
526 #[must_use]
527 fn as_slice(&self) -> &[u16] {
528 match self {
529 Self::Stack(arr) => arr,
530 Self::Heap(_, ptr) => unsafe {
531 std::slice::from_raw_parts(ptr.ptr() as _, self.buf_len())
532 },
533 Self::Unallocated => &[],
534 }
535 }
536
537 #[must_use]
538 const fn buf_len(&self) -> usize {
539 match self {
540 Self::Stack(arr) => arr.len(),
541 Self::Heap(sz_bytes, _) => *sz_bytes / std::mem::size_of::<u16>(),
542 Self::Unallocated => 0,
543 }
544 }
545
546 #[must_use]
547 const fn is_allocated(&self) -> bool {
548 match self {
549 Self::Unallocated => false,
550 _ => true,
551 }
552 }
553
554 #[must_use]
555 fn to_string_checked(&self) -> Result<String, std::string::FromUtf16Error> {
556 match self {
557 Self::Unallocated => Ok(String::new()),
558 _ => String::from_utf16(
559 &self
560 .as_slice()
561 .into_iter()
562 .take_while(|ch| **ch != 0x0000) .map(|ch| *ch)
564 .collect::<Vec<_>>(),
565 ),
566 }
567 }
568}